package com.example.javaproject.thread;

import java.io.UnsupportedEncodingException;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

/**
 * 自己一步步实现锁
 */
public class LockImp {


    public static void main(String[] args) throws InterruptedException, UnsupportedEncodingException {
//        ShareRes.test();
//        M1LockTest.test();
        M2LockTest.test();
    }
}

/**
 * 对单个共享资源的访问，可以利用原子类来实现
 */
class ShareRes {
    private static AtomicInteger a = new AtomicInteger(0);

    public void increment() {
        a.incrementAndGet();
    }

    public int getA() {
        return a.get();
    }


    public static void test() {
        ShareRes sr = new ShareRes();

        Thread thread1 = new Thread(() -> {
            int count = 0;
            while (count < 10) {
                sr.increment();
                System.out.println(sr.getA());
                count++;
            }
        });

        Thread thread2 = new Thread(() -> {
            int count = 0;
            while (count < 10) {
                sr.increment();
                System.out.println(sr.getA());
                count++;
            }
        });

        thread1.start();
        thread2.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("end value = " + sr.getA());
    }
}


interface Lock {
    void lock();

    void unLock();
}


/**
 * 互斥锁的实现例子1，
 */
class M1Lock implements Lock {
    /**
     * 用来记录锁的状态，0是默认状态，1是占用中状态
     */
    private AtomicInteger status = new AtomicInteger(0);

    @Override
    public void lock() {
        // 调用lock的线程获得锁，并将锁的状态改变.
        // 1.先调用lock的线程，cas操作会返回true，那么lock后的代码会继续执行；后调用lock的线程，cas操作使用，一直在这里循环，达到unLock之前其他线程不能访问的目的
        // 2.先调用lock的线程调用unLock后，导致其他线程在这个位置的cas循环跳出，得到真正的执行
        while (!status.compareAndSet(0, 1)) ;
    }

    @Override
    public void unLock() {
        // 释放锁
        status.compareAndSet(1, 0);
    }
}

class M1LockTest {
    private int b = 1;
    private int c = 1;
    private int d = 1;

    private void doWithMultiple() {
        b++;
        c++;
        d++;
    }

    private void printMultiple() {
        System.out.println("b=" + b + ",c=" + c + ",d=" + d);
    }

    public static void test() throws InterruptedException {

        M1LockTest m1LockTest = new M1LockTest();
        Lock m1Lock = new M1Lock();

        new Thread(() -> {
            try {
                m1Lock.lock();
                int count = 0;
                while (count < 3) {
                    m1LockTest.doWithMultiple();
                    count++;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                m1Lock.unLock();
            }
        }).start();

        new Thread(() -> {
            try {
                m1Lock.lock();
                int count = 0;
                while (count < 4) {
                    m1LockTest.doWithMultiple();
                    count++;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                m1Lock.unLock();
            }
        }).start();

        Thread.sleep(2000);
        m1LockTest.printMultiple();
    }


}


/**
 * 以上面M1Lock为例子，当有一个线程获取到锁后，另外一个线程再那里不停的while循环尝试获取锁
 * - 1.如果是多个线程，这样去竞争锁，会浪费cpu（很多线程再那里死循环）
 * - 2.能不能让没有获取到锁的先挂起，当锁被释放后再把它唤醒让它去竞争
 */
class M2Lock implements Lock {
    private AtomicInteger status = new AtomicInteger(0);
    // 阻塞队列
    private LinkedBlockingDeque<Thread> blockingDeque = new LinkedBlockingDeque<>();

    @Override
    public void lock() {
        while (true) {
            // 说明获取到了锁
            if (status.get() > 0) {
                return;
            }
            if (status.compareAndSet(0, 1)) {
                return;
            }
            // 获取锁失败,将线程加入到阻塞队列中
            blockingDeque.add(Thread.currentThread());
            // 挂起当前线程
            LockSupport.park();
        }
    }

    @Override
    public void unLock() {
        if (status.get() > 0) { // 说明当前线程拥有锁
            // 释放锁
            status.compareAndSet(1, 0);
            // 从阻塞队列中取一个线程来执行
            Thread t = blockingDeque.poll();
            if (t != null) {
                LockSupport.unpark(t);
            }
        }
    }
}

class M2LockTest {
    private int b = 1;
    private int c = 1;
    private int d = 1;

    private void doWithMultiple() {
        b++;
        c++;
        d++;
        System.out.println("current thread =" + Thread.currentThread().getName());
    }

    private void printMultiple() {
        System.out.println("b=" + b + ",c=" + c + ",d=" + d);

    }

    public static void test() throws InterruptedException {

        M2LockTest m2LockTest = new M2LockTest();
        Lock m1Lock = new M1Lock();

        new Thread(() -> {
            try {
                m1Lock.lock();
                int count = 0;
                while (count < 3) {
                    m2LockTest.doWithMultiple();
                    count++;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                m1Lock.unLock();
            }
        }, "t1").start();

        new Thread(() -> {
            try {
                m1Lock.lock();
                int count = 0;
                while (count < 4) {
                    m2LockTest.doWithMultiple();
                    count++;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                m1Lock.unLock();
            }
        }, "t2").start();

        new Thread(() -> {
            try {
                m1Lock.lock();
                int count = 0;
                while (count < 4) {
                    m2LockTest.doWithMultiple();
                    count++;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                m1Lock.unLock();
            }
        }, "t3").start();

        Thread.sleep(3000);
        m2LockTest.printMultiple();
    }
}


/**
 * 带有同步策略的锁
 * <p>
 * 1、线程1对变量a进行自增操作，当a增加到10的时候暂停自增。
 * 2、线程2对变量b进行自减操作，当a减到0的时候暂停自减。
 */

interface Condition {
    void waitCondition();

    void notifyCondition();
}

class MCondition implements Condition {

    private M3Lock lock;

    public MCondition(M3Lock lock) {
        this.lock = lock;
    }

    // 等待队列
    LinkedBlockingQueue<Thread> waitList = new LinkedBlockingQueue<>();

    @Override
    public void waitCondition() {
        // 加入到等待队列中
        waitList.add(Thread.currentThread());
        // 释放锁,让其他线程有机会获取锁
        lock.getStatus().compareAndSet(1, 0);

        // 从阻塞队列中获取一个线程执行
        Thread t = lock.getBlockingDeque().poll();
        if (t != null) {
            LockSupport.unpark(t);
        }
        // 阻塞当前线程
        LockSupport.park();
    }

    @Override
    public void notifyCondition() {
        // 当收到通知后，说明该线程条件满足
        Thread t = waitList.poll();
        // 将线程放入到阻塞队列中，让其有机会执行
        if (t != null) {
            lock.getBlockingDeque().offer(t);
        }
    }
}


class M3Lock implements Lock {

    private AtomicInteger status = new AtomicInteger(0);
    // 阻塞队列
    private LinkedBlockingDeque<Thread> blockingDeque = new LinkedBlockingDeque<>();

    public AtomicInteger getStatus() {
        return status;
    }

    public LinkedBlockingDeque<Thread> getBlockingDeque() {
        return blockingDeque;
    }

    public Condition newCondition() {
        return new MCondition(this);
    }

    @Override
    public void lock() {
        while (true) {
            // 说明获取到了锁
            if (status.get() > 0) {
                return;
            }
            if (status.compareAndSet(0, 1)) {
                return;
            }
            // 获取锁失败,将线程加入到阻塞队列中
            blockingDeque.add(Thread.currentThread());
            // 挂起当前线程
            LockSupport.park();
        }
    }

    @Override
    public void unLock() {
        if (status.get() > 0) { // 说明当前线程拥有锁
            // 释放锁
            status.compareAndSet(1, 0);
            // 从阻塞队列中取一个线程来执行
            Thread t = blockingDeque.poll();
            if (t != null) {
                LockSupport.unpark(t);
            }
        }
    }
}


class M3LockTest {

    static int a = 0;
    //构造Lock对象
    static M3Lock lock = new M3Lock();
    static Condition conditionInc = lock.newCondition();
    static Condition conditionSub = lock.newCondition();


    private static void inc() {
        try {
            while (true) {
                //获取锁
                lock.lock();
                {
                    System.out.println("lock suc in " + Thread.currentThread().getName());
                    if (a < 10) {
                        a++;
                        conditionSub.notifyCondition();
                        System.out.println("notify: a:" + a + " in " + Thread.currentThread().getName());
                    } else {
                        //阻塞等待，并释放锁
                        System.out.println("wait: a:" + a + " in " + Thread.currentThread().getName());
                        conditionInc.waitCondition();
                    }
                }
                //释放锁
                lock.unLock();
            }
        } catch (Exception e) {

        }
    }

    private static void sub() {
        try {
            while (true) {
                lock.lock();
                System.out.println("lock suc in " + Thread.currentThread().getName());
                if (a == 0) {
                    System.out.println("wait: a:" + a + " in " + Thread.currentThread().getName());
                    conditionSub.waitCondition();
                } else {
                    a--;
                    conditionInc.notifyCondition();
                    System.out.println("notify: a:" + a + " in " + Thread.currentThread().getName());
                }
                lock.unLock();
            }
        } catch (Exception e) {

        }
    }


    public static void main(String args[]) {
        try {
            Thread t1 = new Thread(() -> inc(), "t1");
            Thread t2 = new Thread(() -> sub(), "t2");
            t1.start();
            t2.start();
        } catch (Exception e) {

        }
    }

}